﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Web;
using System.Web.Mvc;

namespace GroceryPriceBook.Infrastructure
{
	public static class SpanForHelper
	{
		/// <summary>
		/// Returns an HTML span element and the property name of the property that is represented by the specified expression using the span text.
		/// </summary>
		/// 
		/// <returns>
		/// An HTML span element and the property name of the property that is represented by the expression.
		/// </returns>
		/// <param name="html">The HTML helper instance that this method extends.</param>
		/// <param name="expression">An expression that identifies the property to display.</param>
		/// <param name="spanText"></param>
		/// <param name="prefixText">The label text to display.</param>
		/// <param name="postfixText">The label text to display.</param>
		/// <param name="attributes">An object that contains the HTML attributes to set for the element.</param>
		/// <typeparam name="TModel">The type of the model.</typeparam><typeparam name="TValue">The type of the value.</typeparam>
		public static MvcHtmlString SpanFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string spanText = "", string prefixText = "", string postfixText = "", object attributes = null)
		{
			var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
			var htmlFieldName = ExpressionHelper.GetExpressionText(expression);

			var str = spanText;

			// if the spanText is not provided then get the property's DisplayNameAttribute which if not provided use the property's name
			if (string.IsNullOrEmpty(str))
			{
				// from the property's DisplayNameAttribute
				var displayName = metadata.DisplayName;

				// check if the display name attribute wasn't provided
				if (string.IsNullOrEmpty(displayName))
				{
					// the property name as a string
					var propertyName = metadata.PropertyName;

					// if the property's name cannot be converted to a string then use a field from the htmlField array
					str = string.IsNullOrEmpty(propertyName) ? Enumerable.Last<string>((IEnumerable<string>)htmlFieldName.Split(new char[1] { '.' })) : propertyName;
				}
				else
				{
					// the display name attribute was provided - use it
					str = displayName;
				}
			}

			// prefix span text with prefixText if available
			if (!string.IsNullOrEmpty(prefixText))
			{
				str = prefixText + " " + str;
			}

			// append span text with postfixText if available
			if (!string.IsNullOrEmpty(postfixText))
			{
				str = str + " " + postfixText;
			}

			// define the inner text of the span
			var innerText = str;

			// return empty result if nothing provided for span contents
			if (string.IsNullOrEmpty(innerText))
				return MvcHtmlString.Empty;

			var tagBuilder = new TagBuilder("span");
			var idTag = TagBuilder.CreateSanitizedId(html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName));
			tagBuilder.Attributes.Add("id", idTag.Replace("__:index__", "{{:index}}"));

			tagBuilder.SetInnerText(innerText);
			if (attributes != null)
			{
				tagBuilder.MergeAttributes<string, object>((IDictionary<string, object>)HtmlHelper.AnonymousObjectToHtmlAttributes(attributes), true);
			}

			return tagBuilder.ToMvcHtmlString(TagRenderMode.Normal);
		}
	}
}